/*
 * Copyright 2010 Gal Dolber.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.unnison.framework.server.command;

import com.google.inject.Inject;
import com.google.inject.Injector;

import com.unnison.framework.client.command.CommandRpc;
import com.unnison.framework.client.command.action.Action;
import com.unnison.framework.client.command.action.ActionHandler;
import com.unnison.framework.client.command.action.BatchResponse;
import com.unnison.framework.client.command.action.CommandException;
import com.unnison.framework.client.command.action.Handler;
import com.unnison.framework.client.command.action.Response;

import java.util.ArrayList;

/**
 * Service implementation.
 */
public class CommandRpcImpl implements CommandRpc {

    private Injector injector;

    @SuppressWarnings({"unchecked", "rawtypes"})
    @Override
    public Response execute(Action<Response> action) throws CommandException {
        Class<? extends Action> actionClass = action.getClass();
        if (!actionClass.isAnnotationPresent(ActionHandler.class)) {
            throw new IllegalStateException("The action " + actionClass.getCanonicalName() + " don't have the RpcActionHandler annotation");
        }
        try {
            Handler<Action<Response>, Response> rpcHandler = injector.getInstance(actionClass.getAnnotation(ActionHandler.class).value());

            // Handlers routing
            rpcHandler = getRoutedHandler(action, rpcHandler);

            return rpcHandler.handle(action);
        } catch (CommandException e) {
            throw e;
        }

    }

    @Override
    public BatchResponse executeBatch(ArrayList<Action<Response>> actions) {
        BatchResponse batch = new BatchResponse();
        for (Action<Response> action : actions) {
            try {
                batch.add(execute(action));
            } catch (CommandException e) {
                batch.add(e);
            }
        }
        return batch;
    }

    /**
     * Support for multiple layers of routing.
     */
    @SuppressWarnings("unchecked")
    private <A extends Action<R>, R extends Response> Handler<A, R> getRoutedHandler(A action, Handler<A, R> rpcHandler) {
        if (rpcHandler instanceof HandlerRouter) {
            Class<? extends Handler<A, R>> routedClass = ((HandlerRouter<A, R>) rpcHandler).getRoutedHandler(action);
            if (routedClass != null) {
                rpcHandler = injector.getInstance(routedClass);
            } else {
                return rpcHandler;
            }
            return getRoutedHandler(action, rpcHandler);
        }
        return rpcHandler;
    }

    @Inject
    public void injectInjector(Injector injector) {
        this.injector = injector;
    }
}
